#include <net/if.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <arpa/inet.h>

int open_nl()
{
	int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
	if (fd < 0) {
		perror("socket");
		exit(0);
	}
	struct sockaddr_nl addr;
	memset(&addr, 0, sizeof(addr));
	addr.nl_family = AF_NETLINK;
	addr.nl_pid = getpid();
	//addr.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV4_ROUTE | RTMGRP_IPV6_IFADDR | RTMGRP_IPV6_ROUTE;
	if (bind(fd, (void *)&addr, sizeof(addr)) < 0) {
		perror("bind");
		exit(0);
	}
	return fd;
}

int send_req(int fd)
{
	union {
		struct nlmsghdr nh;
		char buf[1024];
	} req;

	memset(&req, 0, sizeof(req));
	req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
	req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
	req.nh.nlmsg_type = RTM_GETROUTE;
	req.nh.nlmsg_seq = 1;

	struct rtmsg *rt = NLMSG_DATA(&req.nh);

	rt->rtm_family = AF_UNSPEC;
	rt->rtm_type = RTN_UNICAST;
	rt->rtm_protocol = RTPROT_KERNEL;
	rt->rtm_table = RT_TABLE_MAIN;

	int len = 0;
	if ((len = send(fd, req.buf, req.nh.nlmsg_len, 0)) < 0) {
		perror("send");
		exit(0);
	}
	return 0;
}

int recv_nl(int fd, char *buf, int len)
{
	int rlen = 0;
	if ((rlen = recv(fd, buf, len, 0)) < 0) {
		perror("recv");
		exit(0);
	}
	return rlen;
}

char *rt_type[] = { "TN_UNSPEC",       "RTN_UNICAST", /* Gateway or direct route	*/
		    "RTN_LOCAL", /* Accept locally		*/
		    "RTN_BROADCAST", /* Accept locally as broadcast,
				   send as broadcast */
		    "RTN_ANYCAST", /* Accept locally as broadcast,
				   but send as unicast */
		    "RTN_MULTICAST", /* Multicast route		*/
		    "RTN_BLACKHOLE", /* Drop				*/
		    "RTN_UNREACHABLE", /* Destination is unreachable   */
		    "RTN_PROHIBIT", /* Administratively prohibited	*/
		    "RTN_THROW", /* Not in this table		*/
		    "RTN_NAT", /* Translate this address	*/
		    "RTN_XRESOLVE", /* Use external resolver	*/
		    "_RTN_MAX" };

char *rtattr_type[] = { "RTA_UNSPEC", //
			"RTA_DST", //
			"RTA_SRC", //
			"RTA_IIF", //
			"RTA_OIF", //
			"RTA_GATEWAY", //
			"RTA_PRIORITY", //
			"RTA_PREFSRC", //
			"RTA_METRICS", //
			"RTA_MULTIPATH", //
			"RTA_PROTOINFO", /* no longer used */
			"RTA_FLOW", //
			"RTA_CACHEINFO", //
			"RTA_SESSION", /* no longer used */
			"RTA_MP_ALGO", /* no longer used */
			"RTA_TABLE", //
			"RTA_MARK", //
			"RTA_MFC_STATS", //
			"RTA_VIA", //
			"RTA_NEWDST", //
			"RTA_PREF", //
			"RTA_ENCAP_TYPE", //
			"RTA_ENCAP", //
			"RTA_EXPIRES", //
			"RTA_PAD", //
			"RTA_UID", //
			"RTA_TTL_PROPAGATE", //
			"RTA_IP_PROTO", //
			"RTA_SPORT", //
			"RTA_DPORT", //
			"RTA_NH_ID", //
			"__RTA_MAX" };

int analysis_attr(struct rtattr *rta, int len, int af)
{
	struct sockaddr_storage *addr;
	int *if_idx = NULL;
	char host[INET6_ADDRSTRLEN + IFNAMSIZ];
	while (RTA_OK(rta, len)) {
		//printf("rta_type=%s %d rta_len=%d\n", rtattr_type[rta->rta_type], rta->rta_type, rta->rta_len);
		switch (rta->rta_type) {
		case RTA_GATEWAY:
			addr = RTA_DATA(rta);
			printf("default via %s", inet_ntop(af, addr, host, sizeof(host)));
			break;
		case RTA_DST:
			addr = RTA_DATA(rta);
			printf("%s ", inet_ntop(af, addr, host, sizeof(host)));
			break;
		case RTA_OIF:
			if_idx = RTA_DATA(rta);
			printf(" dev %s", if_indextoname(*if_idx, host));
			break;
		case RTA_PREFSRC:
			addr = RTA_DATA(rta);
			printf(" src %s", inet_ntop(af, addr, host, sizeof(host)));
			break;
		}
		rta = RTA_NEXT(rta, len);
	}
	printf("\n");
	return 0;
}

int analysis_nl(struct nlmsghdr *rtnh, int len)
{
	if (rtnh->nlmsg_type != RTM_GETROUTE && rtnh->nlmsg_type != RTM_NEWROUTE) {
		return 0;
	}
	for (; NLMSG_OK(rtnh, len); rtnh = NLMSG_NEXT(rtnh, len)) {
		struct rtmsg *rt = NLMSG_DATA(rtnh);
		if (rt->rtm_type != RTN_UNICAST) {
			continue;
		}
		//printf("rt_type=%s\n", rt_type[rt->rtm_type]);
		//printf("route donme = %s\n", rt->rtm_family == AF_INET ? "IPV4" : "IPV6");

		struct rtattr *rta = RTM_RTA(rt);
		analysis_attr(rta, rtnh->nlmsg_len - NLMSG_SPACE(sizeof(struct rtmsg)), rt->rtm_family);
		analysis_attr(rta, RTA_PAYLOAD(rta), rt->rtm_family);
		printf("*************************************\n");
	}
	return 0;
}

int main()
{
	int fd = open_nl();
	send_req(fd);

	union {
		struct nlmsghdr nh;
		char buf[8192];
	} res;

	int len = 0;
	while ((len = recv_nl(fd, res.buf, sizeof(res))) > 0) {
		analysis_nl(&res.nh, len);
	}

	return 0;
}